
import java.util.ArrayList;
import java.util.Random;

@SuppressWarnings("rawtypes")
public abstract class Driver<T extends Driver> {

    protected long id;

    protected Vertex origin;
    protected Vertex destination;

    protected Graph G;

    //list of 'friends'
    protected ArrayList<T> neighbours;

    protected ArrayList<RouteEntry> route;

    protected Vertex curr_intersection;

    protected Edge curr_road;

    protected Random random;

    protected Driver(long id, Vertex origin, Vertex destination, Graph G) {

        if (origin == destination) {
            System.err.println("Origin and destination cannot be the same!");
        }

        this.id = id;
        this.origin = origin;
        this.destination = destination;
        this.G = G;

        this.neighbours = new ArrayList<T>();

        random = new Random(Params.RANDOM.nextLong());
    }

    public long getId() {
        return this.id;
    }

    public Edge getCurrRoad() {
        return curr_road;
    }

    public Vertex getOrigin() {
        return origin;
    }

    public Vertex getDestination() {
        return destination;
    }

    @Override
    public String toString() {
        return toString(true);
    }

    public String toString(boolean printODpair) {
        return "driver" + id + (printODpair ? " (" + origin + destination + ")" : "");
    }

    public ArrayList<T> getNeighbours() {
        return neighbours;
    }

    public void setNeighbours(ArrayList<T> neighbours) {
        this.neighbours = neighbours;
    }

    public void printRoute() {
        if (route.size() < 1) {
            System.out.println(">>>>>>>>>>>>> " + this + " ---- " + route);
        }

        String str = this + ": " + route.get(0).e.getFrom();
        double sum = 0;
        for (RouteEntry e : route) {
            str += " > " + e.e.getTo();
            //System.out.println(e.e + " = " + e.v);
            sum += e.v;
        }
        str += " (total=" + sum + ")";
        //System.out.println("Total = " + sum);
        System.out.println(str);
    }

    public double getTravelTime() {
        double sum = 0;
        for (RouteEntry e : route) {
            sum += e.v;
        }
        return sum;
    }

    /**
     * Actions to be performed by each agent. Note: two steps are available
     * because some operations take into consideration what was made by all
     * agents.
     */
    public abstract void step_A();

    /**
     * Actions that must be performed only after all agents have finished
     * step_A. Note: two steps are available because some operations take into
     * consideration what was made by all agents.
     */
    public abstract void step_B();

    public boolean hasArrived() {
        if (route.size() == 0) {
            return false;
        }
        if (route.get(route.size() - 1).e.getTo() == destination) {
            return true;
        }
        return false;
    }

    public void reset() {
        curr_intersection = origin;
        curr_road = null;

        route = new ArrayList<RouteEntry>();

    }

    public void resetAll() {
        reset();

    }

}
